﻿Imports System.ComponentModel
Imports System.Xml.Serialization
Imports System.Runtime.Serialization

Namespace DataModel

    Public Class SolutionOptions
        Inherits BaseDataModel(Of SolutionOptions)
        Implements IDisposable
        Shared Sub New()
            m_XmlFilename = My.Computer.FileSystem.CombinePath(My.Computer.FileSystem.SpecialDirectories.CurrentUserApplicationData, "SolutionOptions.xml")
        End Sub
        Public Sub New()
            m_UniqueClassId = Guid.NewGuid
            m_AutoSearchDelayTimer = New Timers.Timer With {.Interval = 500}
        End Sub
        Private Shared ReadOnly m_XmlFilename As String
        Private WithEvents m_AutoSearchDelayTimer As Timers.Timer


#Region " Core Data Properties "

        <DataObjectField(True),
         DisplayName("Solution Name"),
         Description("Solution Name"),
         XmlElement("SolutionName"),
         DataMember(Name:="SolutionName"), System.ComponentModel.DataAnnotations.Required(ErrorMessage:="Solutions Name Is Required")>
        Public Property SolutionName() As String
            Get
                Return m_SolutionName
            End Get
            Set(ByVal value As String)
                If m_SolutionName = value Then
                    Return
                End If
                m_SolutionName = value
                OnPropertyChanged("SolutionName")
            End Set
        End Property
        Private m_SolutionName As String

        <DataObjectField(True),
         DisplayName("Root Directory"),
         Description("Root Directory"),
         XmlElement("RootDirectory"),
         DataMember(Name:="RootDirectory"), System.ComponentModel.DataAnnotations.Required(ErrorMessage:="Root Directory Is Required"), System.ComponentModel.DataAnnotations.StringLength(255, ErrorMessage:="The directory is too long")>
        Public Property RootDirectory() As String
            Get
                Return m_RootDirectory
            End Get
            Set(ByVal value As String)
                If m_RootDirectory = value Then
                    Return
                End If
                m_RootDirectory = value
                OnPropertyChanged("RootDirectory")
                If String.IsNullOrWhiteSpace(ZipOutputDirectory) Then
                    ZipOutputDirectory = m_RootDirectory
                End If

            End Set
        End Property
        Private m_RootDirectory As String

        <DataObjectField(True),
         DisplayName("Zip Output Directory"),
         Description("Zip Output Directory"),
         XmlElement("ZipOutputDirectory"),
         DataMember(Name:="ZipOutputDirectory"), System.ComponentModel.DataAnnotations.Required(ErrorMessage:="Zip Output Directory Is Required"), System.ComponentModel.DataAnnotations.StringLength(255, ErrorMessage:="The directory is too long")>
        Public Property ZipOutputDirectory() As String
            Get
                If String.IsNullOrWhiteSpace(m_ZipOutputDirectory) Then
                    m_ZipOutputDirectory = RootDirectory
                End If
                Return m_ZipOutputDirectory
            End Get
            Set(ByVal value As String)
                If m_ZipOutputDirectory = value Then
                    Return
                End If
                m_ZipOutputDirectory = value
                OnPropertyChanged("ZipOutputDirectory")
            End Set
        End Property
        Private m_ZipOutputDirectory As String

        <DataObjectField(True),
         DisplayName("Recurse Through Sub-Dirctories"),
         Description("Recurse Through Sub-Dirctories"),
         XmlElement("RecurseThroughSubDirctories"),
         DataMember(Name:="RecurseThroughSubDirctories")>
        Public Property RecurseThroughSubDirctories() As Boolean
            Get
                Return m_RecurseThroughSubDirctories
            End Get
            Set(ByVal value As Boolean)
                If m_RecurseThroughSubDirctories = value Then
                    Return
                End If
                m_RecurseThroughSubDirctories = value
                OnPropertyChanged("RecurseThroughSubDirctories")
            End Set
        End Property
        Private m_RecurseThroughSubDirctories As Boolean


        <DataObjectField(True),
         DisplayName("Delete .Suo Files"),
         Description("Note when you do delete the Suo File all your open tabs in VS will be gone. Very help full if VS crashes every time you open VS."),
         XmlElement("Suo"),
         DataMember(Name:="Suo")>
        Public Property Suo() As Boolean
            Get
                Return m_Suo
            End Get
            Set(ByVal value As Boolean)
                If m_Suo = value Then
                    Return
                End If

                m_Suo = value
                OnPropertyChanged("Suo")
            End Set
        End Property
        Private m_Suo As Boolean

        <DataObjectField(True),
         DisplayName("Delete .Webinfo Files"),
         Description("May help in opening web application if they will not open."),
         XmlElement("Webinfo"),
         DataMember(Name:="Webinfo")>
        Public Property Webinfo() As Boolean
            Get
                Return m_Webinfo
            End Get
            Set(ByVal value As Boolean)
                If m_Webinfo = value Then
                    Return
                End If

                m_Webinfo = value
                OnPropertyChanged("Webinfo")
            End Set
        End Property
        Private m_Webinfo As Boolean


        <DataObjectField(True),
         DisplayName("Delete .User Files"),
         Description("Can help if VS crashes on open."),
         XmlElement("User"),
         DataMember(Name:="User")>
        Public Property User() As Boolean
            Get
                Return m_User
            End Get
            Set(ByVal value As Boolean)
                If m_User = value Then
                    Return
                End If
                m_User = value
                OnPropertyChanged("User")
            End Set
        End Property
        Private m_User As Boolean

        <DataObjectField(True),
         DisplayName("Delete all files in the Bin directory"),
         Description("Note bad practice to keep externally linked dll in the bin directory. Keep them in a separate out of project folder."),
         XmlElement("CleanBin"),
         DataMember(Name:="CleanBin")>
        Public Property CleanBin() As Boolean
            Get
                Return m_CleanBin
            End Get
            Set(ByVal value As Boolean)
                If m_CleanBin = value Then
                    Return
                End If

                m_CleanBin = value
                OnPropertyChanged("CleanBin")
            End Set
        End Property
        Private m_CleanBin As Boolean

        <DataObjectField(True),
         DisplayName("Delete all files in the Obj directory"),
         Description("Note bad practice to keep externally linked dll in the Obj directory. Keep them in a separate out of project folder."),
         XmlElement("CleanObj"),
         DataMember(Name:="CleanObj")>
        Public Property CleanObj() As Boolean
            Get
                Return m_CleanObj
            End Get
            Set(ByVal value As Boolean)
                If m_CleanObj = value Then
                    Return
                End If
                m_CleanObj = value
                OnPropertyChanged("CleanObj")
            End Set
        End Property
        Private m_CleanObj As Boolean

        <DataObjectField(True),
         DisplayName("Clear Readonly Flag"),
         Description("Clear Readonly Flag on all files under the root directory"),
         XmlElement("ClearReadOnlyFlag"),
         DataMember(Name:="ClearReadOnlyFlag")>
        Public Property ClearReadOnlyFlag() As Boolean
            Get
                Return m_ClearReadOnlyFlag
            End Get
            Set(ByVal value As Boolean)
                If m_ClearReadOnlyFlag = value Then
                    Return
                End If
                m_ClearReadOnlyFlag = value
                OnPropertyChanged("ClearReadOnlyFlag")
            End Set
        End Property
        Private m_ClearReadOnlyFlag As Boolean

        <DataObjectField(True),
         DisplayName("Child Options"),
         Description("Child Options"),
         XmlElement("ChildOptions"),
         DataMember(Name:="ChildOptions")>
        Public ReadOnly Property ChildOptions() As System.Collections.ObjectModel.ObservableCollection(Of SolutionOptions)
            Get
                If m_ChildOptions Is Nothing Then
                    m_ChildOptions = New System.Collections.ObjectModel.ObservableCollection(Of SolutionOptions)
                    AddHandler m_ChildOptions.CollectionChanged, Sub(s, e)
                                                                     OnPropertyChanged("ChildOptions")
                                                                     If e.Action = Specialized.NotifyCollectionChangedAction.Add Then
                                                                         For Each item In e.NewItems
                                                                             Dim folderOptionsItem As SolutionOptions = DirectCast(item, SolutionOptions)
                                                                             folderOptionsItem.m_Parent = Me
                                                                             AddHandler folderOptionsItem.RemoveMe, Sub(sender, eventargs)
                                                                                                                        m_ChildOptions.Remove(folderOptionsItem)
                                                                                                                    End Sub
                                                                         Next
                                                                     End If
                                                                     If e.Action = Specialized.NotifyCollectionChangedAction.Remove Then
                                                                         For Each item In e.OldItems
                                                                             DirectCast(item, SolutionOptions).m_Parent = Nothing
                                                                         Next
                                                                     End If
                                                                 End Sub

                End If
                Return m_ChildOptions
            End Get
        End Property
        Private m_ChildOptions As System.Collections.ObjectModel.ObservableCollection(Of SolutionOptions)

     
#End Region

#Region " Tree Properties "

        <DataObjectField(True),
         DisplayName("Is Selected"),
         Description("Is Selected"),
         XmlIgnore(),
         DataMember(Name:="IsSelected")>
        Public Property IsSelected() As Boolean
            Get
                Return m_IsSelected
            End Get
            Set(ByVal value As Boolean)
                If m_IsSelected = value Then
                    Return
                End If
                m_IsSelected = value
                OnPropertyChanged("IsSelected")
            End Set
        End Property
        Private m_IsSelected As Boolean

        <DataObjectField(True),
         DisplayName("Is Expanded"),
         Description("Is Expanded"),
             XmlIgnore(),
         DataMember(Name:="IsExpanded")>
        Public Property IsExpanded() As Boolean
            Get
                Return m_IsExpanded
            End Get
            Set(ByVal value As Boolean)
                If m_IsExpanded = value Then
                    Return
                End If
                m_IsExpanded = value
                OnPropertyChanged("IsExpanded")
            End Set
        End Property
        Private m_IsExpanded As Boolean

        <DataObjectField(True),
         DisplayName("Search Text"),
         Description("Search Text"),
        XmlIgnore(),
         DataMember(Name:="SearchText")>
        Public Property SearchText() As String
            Get
                Return m_SearchText
            End Get
            Set(ByVal value As String)
                If m_SearchText = value Then
                    Return
                End If
                m_SearchText = value
                OnPropertyChanged("SearchText")
                If Not m_AutoSearchDelayTimer.Enabled Then
                    m_AutoSearchDelayTimer.Start()
                Else
                    m_AutoSearchDelayTimer.Stop()
                    m_AutoSearchDelayTimer.Start()
                End If

            End Set
        End Property
        Private m_SearchText As String
#End Region

#Region " Helper Properties "

        <Xml.Serialization.XmlIgnore()>
        Public ReadOnly Property FileErrorList As System.Collections.ObjectModel.ObservableCollection(Of String)
            Get
                If m_FileErrorList Is Nothing Then
                    m_FileErrorList = New System.Collections.ObjectModel.ObservableCollection(Of String)
                    AddHandler m_FileErrorList.CollectionChanged, Sub(s, e) OnPropertyChanged("FileErrorList")
                End If
                Return m_FileErrorList
            End Get
        End Property
        Private m_FileErrorList As System.Collections.ObjectModel.ObservableCollection(Of String)

        <Xml.Serialization.XmlIgnore(), DisplayName("Current Action")>
        Public ReadOnly Property CurrentAction As String
            Get
                Return m_CurrentAction
            End Get
        End Property
        Private m_CurrentAction As String

        <Xml.Serialization.XmlIgnore()>
        Public ReadOnly Property Parent As SolutionOptions
            Get
                Return m_Parent
            End Get
        End Property
        Private m_Parent As SolutionOptions
#End Region

#Region " Command Properties "

        <Xml.Serialization.XmlIgnore(), DisplayName("..."), Description("Select the root Directory")>
        Public ReadOnly Property OpenFolderCommand() As ICommand
            Get
                If m_OpenFolderCommand Is Nothing Then
                    m_OpenFolderCommand = New Commands.ActionCommand(Sub(params)
                                                                         Using folder As New System.Windows.Forms.FolderBrowserDialog With {.SelectedPath = RootDirectory}
                                                                             If folder.ShowDialog = Forms.DialogResult.OK Then
                                                                                 RootDirectory = folder.SelectedPath
                                                                             End If
                                                                         End Using
                                                                     End Sub)
                End If
                Return m_OpenFolderCommand
            End Get
        End Property
        Private m_OpenFolderCommand As Commands.ActionCommand

        <Xml.Serialization.XmlIgnore(), DisplayName("..."), Description("Select the Zip output Directory")>
        Public ReadOnly Property OpenFolderZipCommand() As ICommand
            Get
                If m_OpenFolderZipCommand Is Nothing Then
                    m_OpenFolderZipCommand = New Commands.ActionCommand(Sub(params)
                                                                            Using folder As New System.Windows.Forms.FolderBrowserDialog With {.SelectedPath = ZipOutputDirectory}
                                                                                If folder.ShowDialog = Forms.DialogResult.OK Then
                                                                                    ZipOutputDirectory = folder.SelectedPath
                                                                                End If
                                                                            End Using
                                                                        End Sub)
                End If
                Return m_OpenFolderZipCommand
            End Get
        End Property
        Private m_OpenFolderZipCommand As Commands.ActionCommand

        <Xml.Serialization.XmlIgnore(), DisplayName("Clean"), Description("Clean Current Configuration")>
        Public ReadOnly Property CleanCommand() As ICommand
            Get
                Dim backgroundworkerIsBusy As Func(Of Boolean) = Function()
                                                                     If Not m_CleanBackgroundWorker Is Nothing Then
                                                                         Return Not m_CleanBackgroundWorker.IsBusy
                                                                     Else
                                                                         Return True
                                                                     End If
                                                                 End Function

                If m_CleanCommand Is Nothing Then
                    m_CleanCommand = New Commands.ActionCommand(Sub(params)
                                                                    Clean()
                                                                End Sub, Function() Not SomeSolutionOptionsHasErrors() And backgroundworkerIsBusy())
                    AddHandler ItemsValidationHasErrorsHasChanged, Sub(s, e) m_CleanCommand.OnCanExecuteChanged(s, e)
                    AddHandler CleanBackgroundWorkerStatusChanged, Sub(s, e) m_CleanCommand.OnCanExecuteChanged(s, e)
                End If
                Return m_CleanCommand
            End Get
        End Property
        Private m_CleanCommand As Commands.ActionCommand

        <Xml.Serialization.XmlIgnore(), DisplayName("Save"), Description("Save Changes")>
        Public ReadOnly Property SaveCommand() As ICommand
            Get
                If m_SaveCommand Is Nothing Then
                    m_SaveCommand = New Commands.ActionCommand(Sub(params)
                                                                   Save()
                                                               End Sub, Function() Not SomeSolutionOptionsHasErrors())
                    AddHandler ItemsValidationHasErrorsHasChanged, Sub(s, e) m_SaveCommand.OnCanExecuteChanged(s, e)
                End If
                Return m_SaveCommand
            End Get
        End Property
        Private m_SaveCommand As Commands.ActionCommand

        <Xml.Serialization.XmlIgnore(), DisplayName("Add"), Description("Add New Configuration")>
        Public ReadOnly Property AddChildCommand() As ICommand
            Get
                If m_AddChildCommand Is Nothing Then
                    m_AddChildCommand = New Commands.ActionCommand(Sub(params)
                                                                       AddChild()
                                                                   End Sub, Function() Not SomeSolutionOptionsHasErrors())
                    AddHandler ItemsValidationHasErrorsHasChanged, Sub(s, e) m_SaveCommand.OnCanExecuteChanged(s, e)
                End If
                Return m_AddChildCommand
            End Get
        End Property
        Private m_AddChildCommand As Commands.ActionCommand
        <Xml.Serialization.XmlIgnore(), DisplayName("Remove"), Description("Remove This Configuration")>
        Public ReadOnly Property RemoveMeCommand() As ICommand
            Get
                If m_RemoveMeCommand Is Nothing Then
                    m_RemoveMeCommand = New Commands.ActionCommand(Sub(params)
                                                                       RaiseEvent RemoveMe(Me, EventArgs.Empty)
                                                                   End Sub, Function() Not SomeSolutionOptionsHasErrors())
                    AddHandler ItemsValidationHasErrorsHasChanged, Sub(s, e) m_SaveCommand.OnCanExecuteChanged(s, e)

                End If
                Return m_RemoveMeCommand
            End Get
        End Property
        Private m_RemoveMeCommand As Commands.ActionCommand

        <Xml.Serialization.XmlIgnore(), DisplayName("Populate"), Description("Recurse Sub Directories to populate tree")>
        Public ReadOnly Property PopulateCommand() As ICommand
            Get
                Dim backgroundworkerIsBusy As Func(Of Boolean) = Function()
                                                                     If Not m_PopulateBackgroundWorker Is Nothing Then
                                                                         Return Not m_PopulateBackgroundWorker.IsBusy
                                                                     Else
                                                                         Return True
                                                                     End If
                                                                 End Function
                If m_PopulateCommand Is Nothing Then
                    m_PopulateCommand = New Commands.ActionCommand(Sub(params)
                                                                       Populate()
                                                                   End Sub, Function() Not SomeSolutionOptionsHasErrors() And backgroundworkerIsBusy())
                    AddHandler ItemsValidationHasErrorsHasChanged, Sub(s, e) m_SaveCommand.OnCanExecuteChanged(s, e)
                    AddHandler PopulateBackgroundChanged, Sub(s, e) m_SaveCommand.OnCanExecuteChanged(s, e)
                End If
                Return m_PopulateCommand
            End Get
        End Property
        Private m_PopulateCommand As Commands.ActionCommand


        <Xml.Serialization.XmlIgnore(), DisplayName("Set Children"), Description("Set All Children options to match this.")>
        Public ReadOnly Property SetChildernCommand() As ICommand
            Get
                If m_SetChildernCommand Is Nothing Then
                    m_SetChildernCommand = New Commands.ActionCommand(Sub(params)
                                                                          SetChildern()
                                                                      End Sub)

                End If
                Return m_SetChildernCommand
            End Get
        End Property
        Private m_SetChildernCommand As Commands.ActionCommand
        <Xml.Serialization.XmlIgnore(), DisplayName("Search"), Description("Search for the solution name with this text in it.")>
        Public ReadOnly Property SearchCommand() As ICommand
            Get
                If m_SearchCommand Is Nothing Then
                    m_SearchCommand = New Commands.ActionCommand(Sub(params)
                                                                     PerformSearch()
                                                                 End Sub)

                End If
                Return m_SearchCommand
            End Get
        End Property
        Private m_SearchCommand As Commands.ActionCommand
        <Xml.Serialization.XmlIgnore(), DisplayName("Zip"), Description("Zip all files under the root directory")>
        Public ReadOnly Property ZipCommand() As ICommand
            Get
                If m_ZipCommand Is Nothing Then
                    m_ZipCommand = New Commands.ActionCommand(Sub(params)
                                                                  ZipSolution()
                                                              End Sub)

                End If
                Return m_ZipCommand
            End Get
        End Property
        Private m_ZipCommand As Commands.ActionCommand
#End Region

#Region " Command Actions "

        Public Event RemoveMe As EventHandler

        Public Sub SetChildern()
            ChildOptions.AsParallel.ForAll(Sub(item)
                                               item.RecurseThroughSubDirctories = RecurseThroughSubDirctories
                                               item.Suo = Suo
                                               item.Webinfo = Webinfo
                                               item.CleanBin = CleanBin
                                               item.CleanObj = CleanObj
                                               item.User = User
                                               item.ClearReadOnlyFlag = ClearReadOnlyFlag
                                               item.SetChildern()
                                           End Sub)
        End Sub

        Private Sub ZipSolution()
            Utilities.ZipFolder.CreateZipFromFolder(My.Computer.FileSystem.CombinePath(ZipOutputDirectory, String.Format("{0}.zip", SolutionName)), RootDirectory, IO.Packaging.CompressionOption.Fast)
        End Sub

        Private m_PopulateBackgroundWorker As BackgroundWorker
        Public Sub Populate()

            If m_PopulateBackgroundWorker Is Nothing Then
                m_PopulateBackgroundWorker = New BackgroundWorker With {.WorkerReportsProgress = True}
                AddHandler m_PopulateBackgroundWorker.DoWork, Sub(s, e)
                                                                  Dim fileAction As Action(Of SolutionOptions, String) = Sub(rootfolder, filename)
                                                                                                                             Dim fileinfo As IO.FileInfo = My.Computer.FileSystem.GetFileInfo(filename)
                                                                                                                             Dim folderOption As New SolutionOptions With {.SolutionName = fileinfo.Name.Replace(fileinfo.Extension, ""), .RootDirectory = fileinfo.DirectoryName}
                                                                                                                             rootfolder.ChildOptions.Add(folderOption)
                                                                                                                         End Sub

                                                                  Utilities.FileTools.ProcessFilesRecursively(RootDirectory, "*.sln", Sub(filename)
                                                                                                                                          Dim fileinfo As IO.FileInfo = My.Computer.FileSystem.GetFileInfo(filename)
                                                                                                                                          Dim folderOption As New SolutionOptions With {.SolutionName = fileinfo.Name.Replace(fileinfo.Extension, ""), .RootDirectory = fileinfo.DirectoryName}

                                                                                                                                          fileinfo.Directory.GetDirectories().ToList.ForEach(Sub(dirctoryName)
                                                                                                                                                                                                 Utilities.FileTools.ProcessFilesRecursively(dirctoryName.FullName, "*.sln", Sub(subfileName)
                                                                                                                                                                                                                                                                                 fileAction(folderOption, subfileName)
                                                                                                                                                                                                                                                                             End Sub, IO.SearchOption.TopDirectoryOnly)
                                                                                                                                                                                             End Sub)

                                                                                                                                          Dim response As New Tuple(Of SolutionOptions, String)(folderOption, fileinfo.DirectoryName)
                                                                                                                                          m_PopulateBackgroundWorker.ReportProgress(0, response)
                                                                                                                                      End Sub, IO.SearchOption.TopDirectoryOnly)

                                                              End Sub
                AddHandler m_PopulateBackgroundWorker.ProgressChanged, Sub(s, e)
                                                                           Dim response As Tuple(Of SolutionOptions, String) = DirectCast(e.UserState, Tuple(Of SolutionOptions, String))
                                                                           If response.Item1 IsNot Nothing Then
                                                                               ChildOptions.Add(response.Item1)
                                                                           End If
                                                                           SetCurrentAction(response.Item2)

                                                                       End Sub
                AddHandler m_PopulateBackgroundWorker.RunWorkerCompleted, Sub(s, e)
                                                                              m_PopulateBackgroundWorker.Dispose()
                                                                              m_PopulateBackgroundWorker = Nothing
                                                                              RaiseEvent PopulateBackgroundChanged(Me, EventArgs.Empty)
                                                                          End Sub
            End If
            If m_PopulateBackgroundWorker.IsBusy Then
                Return
            End If


            m_PopulateBackgroundWorker.RunWorkerAsync()
            RaiseEvent PopulateBackgroundChanged(Me, EventArgs.Empty)
        End Sub

        Private Event PopulateBackgroundChanged As EventHandler

        Public Sub AddChild()
            Dim newChild As New SolutionOptions
            ChildOptions.Add(newChild)
            newChild.IsExpanded = True
            newChild.IsSelected = True
        End Sub

        Public Sub Save()
            If ItemsValidationHasErrors Then
                Return
            End If
            If Parent IsNot Nothing Then
                Parent.Save()
            Else
                If My.Computer.FileSystem.FileExists(m_XmlFilename) Then
                    My.Computer.FileSystem.DeleteFile(m_XmlFilename)
                End If
                My.Computer.FileSystem.WriteAllText(m_XmlFilename, Me.SerializeToXMLString(), False)
            End If
        End Sub


        Private m_CleanBackgroundWorker As BackgroundWorker
        Public Sub Clean()


            If ItemsValidationHasErrors Then
                Return
            End If
            If m_CleanBackgroundWorker Is Nothing Then
                m_CleanBackgroundWorker = New BackgroundWorker With {.WorkerReportsProgress = True}
                AddHandler m_CleanBackgroundWorker.DoWork, Sub(s, e)
                                                               Dim response As Tuple(Of String, String)
                                                               Dim deleteAction As Action(Of String) = Sub(filename)
                                                                                                           Try
                                                                                                               My.Computer.FileSystem.DeleteFile(filename, FileIO.UIOption.OnlyErrorDialogs, FileIO.RecycleOption.DeletePermanently, FileIO.UICancelOption.ThrowException)
                                                                                                           Catch ex As Exception
                                                                                                               response = New Tuple(Of String, String)("", ex.Message)
                                                                                                               m_CleanBackgroundWorker.ReportProgress(-1, response)
                                                                                                           End Try

                                                                                                       End Sub
                                                               Dim searchOption As IO.SearchOption = IO.SearchOption.AllDirectories
                                                               If Suo Then
                                                                   response = New Tuple(Of String, String)("Deleting Suo Files", "")
                                                                   m_CleanBackgroundWorker.ReportProgress(1, response)
                                                                   Utilities.FileTools.ProcessFilesRecursively(RootDirectory, "*.suo", deleteAction, searchOption)
                                                               End If
                                                               If Webinfo Then
                                                                   response = New Tuple(Of String, String)("Deleting Webinfo Files", "")
                                                                   m_CleanBackgroundWorker.ReportProgress(2, response)
                                                                   Utilities.FileTools.ProcessFilesRecursively(RootDirectory, "*.Webinfo", deleteAction, searchOption)
                                                               End If
                                                               If User Then
                                                                   response = New Tuple(Of String, String)("Deleting .User Files", "")
                                                                   m_CleanBackgroundWorker.ReportProgress(3, response)
                                                                   Utilities.FileTools.ProcessFilesRecursively(RootDirectory, "*.user", deleteAction, searchOption)
                                                               End If
                                                               If CleanBin Then
                                                                   response = New Tuple(Of String, String)("Deleting Bin Files", "")
                                                                   m_CleanBackgroundWorker.ReportProgress(4, response)
                                                                   IO.Directory.GetDirectories(RootDirectory, "Bin", searchOption).AsParallel.ForAll(Sub(directory) Utilities.FileTools.ProcessFilesRecursively(directory, "*.*", deleteAction))
                                                               End If
                                                               If CleanObj Then
                                                                   response = New Tuple(Of String, String)("Deleting Obj Files", "")
                                                                   m_CleanBackgroundWorker.ReportProgress(5, response)
                                                                   IO.Directory.GetDirectories(RootDirectory, "obj", searchOption).AsParallel.ForAll(Sub(directory) Utilities.FileTools.ProcessFilesRecursively(directory, "*.*", deleteAction))
                                                               End If
                                                               If ClearReadOnlyFlag Then
                                                                   response = New Tuple(Of String, String)("Clearing Readonly Flag", "")
                                                                   m_CleanBackgroundWorker.ReportProgress(6, response)
                                                                   Utilities.FileTools.ProcessFilesRecursively(RootDirectory, "*.*", Sub(filename) My.Computer.FileSystem.GetFileInfo(filename).Attributes = IO.FileAttributes.Normal)
                                                               End If
                                                           End Sub
                AddHandler m_CleanBackgroundWorker.ProgressChanged, Sub(s, e)
                                                                        Dim response As Tuple(Of String, String) = DirectCast(e.UserState, Tuple(Of String, String))
                                                                        If e.ProgressPercentage > 0 Then
                                                                            SetCurrentAction(response.Item1)
                                                                        Else
                                                                            FileErrorList.Add(response.Item2)
                                                                        End If
                                                                    End Sub
                AddHandler m_CleanBackgroundWorker.RunWorkerCompleted, Sub(s, e)
                                                                           m_CleanBackgroundWorker.Dispose()
                                                                           m_CleanBackgroundWorker = Nothing
                                                                           SetCurrentAction("")
                                                                           RaiseEvent CleanBackgroundWorkerStatusChanged(Me, EventArgs.Empty)
                                                                       End Sub
            End If
            If m_CleanBackgroundWorker.IsBusy Then
                Return
            End If
            FileErrorList.Clear()
            If RecurseThroughSubDirctories Then
                ChildOptions.AsParallel.ForAll(Sub(item)
                                                   Dim oldRecurs As Boolean = item.RecurseThroughSubDirctories
                                                   item.RecurseThroughSubDirctories = True
                                                   item.Clean()
                                                   item.RecurseThroughSubDirctories = oldRecurs
                                               End Sub)
            End If
            If CleanBin Or CleanObj Then
                Dim message As New System.Text.StringBuilder("Are you sure you want to ")
                If CleanBin And Not CleanObj Then
                    message.Append("Delete all files out of the bin directory?")
                ElseIf Not CleanBin And CleanObj Then
                    message.Append("Delete all files out of the obj directory?")
                Else
                    message.Append("Delete all files out of the bin and the obj directory?")
                End If
                If MessageBox.Show(message.ToString, "Delete Confirmation", MessageBoxButton.YesNo, MessageBoxImage.Question) = MessageBoxResult.Yes Then
                    m_CleanBackgroundWorker.RunWorkerAsync()
                    RaiseEvent CleanBackgroundWorkerStatusChanged(Me, EventArgs.Empty)
                End If
            Else
                m_CleanBackgroundWorker.RunWorkerAsync()
                RaiseEvent CleanBackgroundWorkerStatusChanged(Me, EventArgs.Empty)
            End If

        End Sub
        Private Event CleanBackgroundWorkerStatusChanged As EventHandler
        Private Sub SetCurrentAction(actionitem As String)
            If m_CurrentAction = actionitem Then
                Return
            End If
            m_CurrentAction = actionitem
            OnPropertyChanged("CurrentAction")
        End Sub
#End Region

#Region " Validation Helpers "

        <DataObjectField(True),
         DisplayName("ValidationHasErrors"),
         Description("ValidationHasErrors"),
         XmlIgnore()>
        Public Shared Property ItemsValidationHasErrors() As Boolean
            Get
                Return m_ItemsValidationHasErrors
            End Get
            Set(ByVal value As Boolean)
                If m_ItemsValidationHasErrors = value Then
                    Return
                End If
                m_ItemsValidationHasErrors = value
                RaiseEvent ItemsValidationHasErrorsHasChanged(Nothing, EventArgs.Empty)
            End Set
        End Property
        Private Shared m_ItemsValidationHasErrors As Boolean
        Public Shared Event ItemsValidationHasErrorsHasChanged As EventHandler

        Private ReadOnly m_UniqueClassId As Guid
        Public Overrides Function ColumnErrors() As Dictionary(Of String, Dictionary(Of String, String))
            If ClassColumnErrors.ContainsKey(m_UniqueClassId) Then
                Return ClassColumnErrors(m_UniqueClassId)
            End If
            Dim columnErrorsDict As New Dictionary(Of String, Dictionary(Of String, String))
            ClassColumnErrors.Add(m_UniqueClassId, columnErrorsDict)
            Return columnErrorsDict
        End Function

        Private Shared Function SomeSolutionOptionsHasErrors() As Boolean
            Return ClassColumnErrors.Where(Function(columnErrors) columnErrors.Value.Count > 0).Count > 0
        End Function

        Private Shared Function ClassColumnErrors() As Dictionary(Of Guid, Dictionary(Of String, Dictionary(Of String, String)))
            If m_ClassColumnErrors Is Nothing Then
                m_ClassColumnErrors = New Dictionary(Of Guid, Dictionary(Of String, Dictionary(Of String, String)))()
            End If
            Return m_ClassColumnErrors
        End Function
        Private Shared m_ClassColumnErrors As Dictionary(Of Guid, Dictionary(Of String, Dictionary(Of String, String)))

        Private Sub OnValidationErrorsHasChanged(sender As Object, e As EventArgs) Handles Me.ValidationErrorsHasChanged
            If ItemsValidationHasErrors <> ValidationHasErrors Then
                OnPropertyChanged("ItemsValidationHasErrors")
                ItemsValidationHasErrors = ColumnErrors.Count > 0
            End If
        End Sub
#End Region

#Region " Search Methods "

        Private m_MatchingFolderOptions As IEnumerator(Of SolutionOptions)
        Private Sub PerformSearch()
            If m_MatchingFolderOptions Is Nothing OrElse Not m_MatchingFolderOptions.MoveNext() Then
                FindMatches()
            End If

            Dim folderOptions = m_MatchingFolderOptions.Current

            If folderOptions Is Nothing Then
                Return
            End If

            ' Ensure that this person is in view.
            If folderOptions.Parent IsNot Nothing Then
                folderOptions.Parent.IsExpanded = True
            End If

            folderOptions.IsSelected = True
        End Sub
        Private Sub FindMatches()
            Dim matches = FindMatches(SearchText, Me)
            m_MatchingFolderOptions = matches.GetEnumerator()

            If Not m_MatchingFolderOptions.MoveNext() Then
                MessageBox.Show("No matching names were found.", "Try Again", MessageBoxButton.OK, MessageBoxImage.Information)
            End If
        End Sub

        Private Function FindMatches(searchText As String, folderOptions As SolutionOptions) As IEnumerable(Of SolutionOptions)
            Dim items As New List(Of SolutionOptions)()
            If Not String.IsNullOrWhiteSpace(folderOptions.SolutionName) AndAlso folderOptions.SolutionName.Contains(searchText) Then
                items.Add(folderOptions)
            End If

            For Each child As SolutionOptions In folderOptions.ChildOptions
                For Each match As SolutionOptions In Me.FindMatches(searchText, child)
                    items.Add(match)
                Next
            Next
            Return items
        End Function

        Private Sub OnAutoSearchDelayTimerElapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles m_AutoSearchDelayTimer.Elapsed
            m_AutoSearchDelayTimer.Stop()
            m_MatchingFolderOptions = Nothing
            PerformSearch()
        End Sub
#End Region

#Region " Dispose "

        Public Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
            GC.SuppressFinalize(Me)
        End Sub

        Protected Overridable Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If m_AutoSearchDelayTimer IsNot Nothing Then
                    m_AutoSearchDelayTimer.Dispose()
                    m_AutoSearchDelayTimer = Nothing
                End If
                If m_PopulateBackgroundWorker IsNot Nothing Then
                    m_PopulateBackgroundWorker.Dispose()
                    m_PopulateBackgroundWorker = Nothing
                End If
                If m_CleanBackgroundWorker IsNot Nothing Then
                    m_CleanBackgroundWorker.Dispose()
                    m_CleanBackgroundWorker = Nothing
                End If
                If m_MatchingFolderOptions IsNot Nothing Then
                    m_MatchingFolderOptions.Dispose()
                    m_MatchingFolderOptions = Nothing
                End If
            End If
        End Sub

        Protected Overrides Sub Finalize()
            Dispose(False)
        End Sub

#End Region

     
    End Class

End Namespace